﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;

namespace Assimp.Unmanaged {
    internal sealed class UnmanagedLinuxLibdlLibraryImplementation : UnmanagedLibraryImplementation {
        public override String DllExtension {
            get {
                return ".so";
            }
        }

        public override String DllPrefix {
            get {
                return "lib";
            }
        }

        public UnmanagedLinuxLibdlLibraryImplementation(String defaultLibName, Type[] unmanagedFunctionDelegateTypes)
            : base(defaultLibName, unmanagedFunctionDelegateTypes) {
        }

        protected override IntPtr NativeLoadLibrary(String path) {
            IntPtr libraryHandle = dlopen(path, RTLD_NOW);

            if (libraryHandle == IntPtr.Zero && ThrowOnLoadFailure) {
                IntPtr errPtr = dlerror();
                String msg = Marshal.PtrToStringAnsi(errPtr);
                if (!String.IsNullOrEmpty(msg))
                    throw new AssimpException(String.Format("Error loading unmanaged library from path: {0}\n\n{1}", path, msg));
                else
                    throw new AssimpException(String.Format("Error loading unmanaged library from path: {0}", path));
            }

            return libraryHandle;
        }

        protected override IntPtr NativeGetProcAddress(IntPtr handle, String functionName) {
            return dlsym(handle, functionName);
        }

        protected override void NativeFreeLibrary(IntPtr handle) {
            dlclose(handle);
        }

        #region Native Methods

        [DllImport("libdl.so")]
        private static extern IntPtr dlopen(String fileName, int flags);

        [DllImport("libdl.so")]
        private static extern IntPtr dlsym(IntPtr handle, String functionName);

        [DllImport("libdl.so")]
        private static extern int dlclose(IntPtr handle);

        [DllImport("libdl.so")]
        private static extern IntPtr dlerror();

        private const int RTLD_NOW = 2;

        #endregion
    }
}
